﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using AmiEmuLib.Configuration;
using AmiEmuLib.Configuration.AmigaEventsConfig;
using System.Xml;
using System.Diagnostics;
using AmiEmuLib.Host.HostInput;
using System.Collections.ObjectModel;
using AmiEmuLib.AmigaConfiguration;
using log4net;
using TomanuExtensions;

namespace AmiEmuLib.Configuration.EventBindingConfig
{
    /// <summary>
    /// Bind host events with amiga event.
    /// </summary>
    [DebuggerDisplay("AmigaEvent = {AmigaEvent.Name}")]
    public class EventBinding
    {
        /// <summary>
        /// Logger.
        /// </summary>
        private static readonly ILog Logger =
            LogManager.GetLogger(typeof(EventBinding).GetParentFullName());

        /// <summary>
        /// Maximum combinations to store.
        /// </summary>
        public const int MAX_COMBINATIONS_COUNT = 2;

        /// <summary>
        /// Amiga input event.
        /// </summary>
        public readonly AmigaEvent AmigaEvent;

        /// <summary>
        /// Events combinations.
        /// </summary>
        private List<HostEventsCombination> m_combinations = new List<HostEventsCombination>();

        /// <summary>
        /// Is read only. We don't write this information. 
        /// Only deafult amiga events set this to true. 
        /// </summary>
        public readonly bool ReadOnly;

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="a_reader"></param>
        /// <param name="a_events_binding"></param>
        internal EventBinding(XmlReader a_reader, bool a_read_only)
        {
            ReadOnly = a_read_only;

            a_reader.ReadStartElement(GetType().Name);

            string amiga_event_name = a_reader.GetAttribute("Name");
            AmigaEvent = AmigaEventsList.GetEvent(amiga_event_name);
            if (AmigaEvent == null)
                Logger.ErrorFormat("Cannot find amiga event: {0}", amiga_event_name);

            a_reader.MoveToNextElement(typeof(AmigaEvent).Name);

            if (a_reader.IsEmptyElement)
                a_reader.MoveToNextElement("HostEventsCombinations");
            else
            {
                a_reader.ReadStartElement("HostEventsCombinations");

                while (a_reader.IsStartElement())
                    m_combinations.Add(new HostEventsCombination(a_reader, a_read_only));

                m_combinations.RemoveAll(c => c.Combination.Count() == 0);

                a_reader.ReadEndElement();
            }

            if (m_combinations.Count > MAX_COMBINATIONS_COUNT)
            {
                Logger.ErrorFormat("Too much combinations");
                m_combinations = m_combinations.Take(MAX_COMBINATIONS_COUNT).ToList();
            }

            a_reader.ReadEndElement();
        }

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="a_source"></param>
        internal EventBinding(EventBinding a_source)
        {
            AmigaEvent = a_source.AmigaEvent;
            ReadOnly = false;

            foreach (var comb in a_source.Combinations)
                m_combinations.Add(new HostEventsCombination(comb));
        }

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="a_event"></param>
        internal EventBinding(AmigaEvent a_event)
        {
            AmigaEvent = a_event;
            ReadOnly = false;
        }

        /// <summary>
        /// Events combinations.
        /// </summary>
        public IEnumerable<HostEventsCombination> Combinations
        {
            get
            {
                return m_combinations;
            }
        }

        /// <summary>
        /// Write xml.
        /// </summary>
        /// <param name="a_writer"></param>
        internal void WriteXml(XmlWriter a_writer)
        {
            a_writer.WriteStartElement(GetType().Name);

            AmigaEvent.WriteXml(a_writer);

            a_writer.WriteStartElement("HostEventsCombinations");

            foreach (var combination in m_combinations)
                combination.WriteXml(a_writer);

            a_writer.WriteEndElement();

            a_writer.WriteEndElement();
        }

        /// <summary>
        /// Update combination.
        /// </summary>
        /// <param name="a_comb_index"></param>
        /// <param name="a_combination"></param>
        public bool UpdateCombination(int a_comb_index, IEnumerable<HostEvent> a_combination)
        {
            if (ReadOnly)
            {
                Logger.Error("EventBinding is read only.");
                return false;
            }

            if (AmigaEvent.HideFromMapping)
            {
                Logger.Error("Trying to modify HideFromMapping event.");
                return false;
            }

            if ((a_combination != null) && (a_combination.Count() == 0))
                a_combination = null;

            if (a_comb_index >= MAX_COMBINATIONS_COUNT)
            {
                Logger.Error("Too many combinations.");
                return false;
            }

            if (a_comb_index < 0)
            {
                Logger.Error("Index out of range.");
                return false;
            }

            if (a_comb_index >= m_combinations.Count)
            {
                if (a_combination == null)
                    return false;

                m_combinations.Add(new HostEventsCombination());
            }

            if (a_combination == null)
            {
                m_combinations.RemoveAt(a_comb_index);
                return true;
            }

            return m_combinations[a_comb_index].UpdateCombination(a_combination);
        }
    }
}
